package club.jdiy.dev.controller;

import club.jdiy.admin.interceptor.GuestDisabled;
import club.jdiy.core.AppContext;
import club.jdiy.core.base.domain.DictInfo;
import club.jdiy.core.base.domain.Pager;
import club.jdiy.core.base.domain.Ret;
import club.jdiy.core.sql.Args;
import club.jdiy.core.sql.Dao;
import club.jdiy.core.sql.Rs;
import club.jdiy.dev.entity.JDiyUi;
import club.jdiy.dev.meta.ButtonMeta;
import club.jdiy.dev.meta.ColumnMeta;
import club.jdiy.dev.meta.TreeUiMeta;
import club.jdiy.dev.service.JDiyUiService;
import club.jdiy.dev.types.BtnActTpl;
import club.jdiy.dev.view.*;
import club.jdiy.utils.ArrayUtils;
import club.jdiy.utils.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.stream.Collectors;

@Controller
@RequestMapping("mgmt/JDiy")
public class JDiyTrController extends JDiyUiCtrl<JDiyUi, JDiyUiService> implements JDiyController {

    @RequestMapping("{_referrerMenuId}/tree.{opType}.{uid}")
    public String tr(HttpServletResponse response,
                     @RequestParam Map<String, String> qo,
                     @PathVariable String uid,
                     @PathVariable String opType, //打开类型：dialog | tab
                     String openNodeId,//记录（弹窗修改时）的节点id，以便修改完成后关闭弹窗后，更新treeTable并且让刚刚修改的这个节点的祖父级都处于展开状态
                     @PathVariable String _referrerMenuId,//来源主菜单(for权限控制)
                     ModelMap map) throws Exception {
        TreeUiMeta uiMeta;
        try {
            uiMeta = service.getUiMeta(uid);
        } catch (Exception ex) {
            ex.printStackTrace();
            return Ret.direct(response, Ret.Type.msg, "<div style=\"margin:10px;color:red;\">目标界面配置有误，请跟踪控制台错误日志并检查！</div>");
        }
        FtlParser parser = createFtlParser();
        parser.addVariable("qo", qo);

        preUiMeta(uiMeta, parser);

        //buttons预处理：
        boolean hasBat = false;
        List<ButtonMeta> topBtns = new ArrayList<>();
        for (ButtonMeta it : uiMeta.getTopButtons()) {
            if ((!StringUtils.hasText(it.getConditionShow()) || JDiyLsController.ifCondition(it, uiMeta, null, parser))
                    && (!StringUtils.hasText(it.getGrantCode()) || context.hasGrant(_referrerMenuId + ":" + it.getGrantCode()))) {
                it.setTitle(parser.parse(it.getTitle()));
                it.setPageParam(parser.parse(it.getPageParam()));
                if (BtnActTpl.link == it.getAct()) it.setOutLink(parser.parse(it.getOutLink()));
                else if (BtnActTpl.ajax == it.getAct()) {
                    it.setAjaxUrl(parser.parse(it.getAjaxUrl()));
                }
                topBtns.add(it);
                hasBat = hasBat || BtnActTpl.del == it.getAct() || BtnActTpl.update == it.getAct() || BtnActTpl.exec == it.getAct() || BtnActTpl.ajax == it.getAct();
            }
        }
        List<ButtonMeta> rowButtonMeta = Arrays.stream(uiMeta.getRowButtons())
                .filter(it -> !StringUtils.hasText(it.getGrantCode()) || context.hasGrant(_referrerMenuId + ":" + it.getGrantCode()))
                .collect(Collectors.toList());
        if (rowButtonMeta.size() < 1) uiMeta.setMge(false);
        map.put("topBtns", topBtns);
        map.put("rowBtns", rowButtonMeta);
        map.put("hasBat", hasBat);

        int iconIndex = 0;//显示树结构的列索引
        if (uiMeta.isRowNum()) iconIndex++;
        int i = 0;
        for (ColumnMeta col : uiMeta.getColumns()) {
            i++;
            if (uiMeta.getNameField().equals(col.getField())) {
                iconIndex += i;
            }
        }
        map.put("uiMeta", uiMeta);
        map.put("opType", opType);

        map.put("iconIndex", iconIndex);
        map.put("openNodeId", openNodeId);
        map.put("_referrerMenuId", _referrerMenuId);

        try {
            List<String> qols = new ArrayList<>();
            qo.forEach((k, v) -> qols.add(k + "=" + v));
            if (qols.size() > 0) map.put("qoSerialized", ArrayUtils.join(qols, "&"));//将当前界面参数串化，以便传入getTreeData
        } catch (Exception ignore) {
        }
        return "tree.render";
    }

    @RequestMapping("tree.data.{uid}")
    @ResponseBody
    public Ret<?> getTreeData(@RequestParam Map<String, String> qo,
                              @PathVariable String uid, String parentId,
                              @RequestParam(defaultValue = "1") Integer _trDepth,
                              String openNodeId //记录（弹窗修改时）的节点id，以便修改完成后关闭弹窗后，更新treeTable并且让刚刚修改的这个节点的祖父级都处于展开状态
    ) {
        try {
            TreeUiMeta uiMeta;
            try {
                uiMeta = service.getUiMeta(uid);
            } catch (Exception ex) {
                return Ret.error(ex);
            }
            FtlParser parser = createFtlParser();
            parser.addVariable("qo", qo);
            TreeHandler handler = handlers.getHandler(uiMeta.getPageHandler(), TreeHandler.class).orElse(new DefaultTreeHandler());
            Map<String, Map<String, DictInfo>> kvMaps = new HashMap<>();

            kvCachedWrap(kvMaps, uiMeta.getColumns());
            Args args = __createQuery(uiMeta, parser, parentId);

            Dao dao = appContext.getDao();
            Pager<Rs> ls = dao.ls(args);

            Map<String, Rs> _ktbmap = new HashMap<>();
            List<Rs> dataList = __wrapRowData(uiMeta, parser, handler, ls.getItems(), kvMaps, _ktbmap,
                    dao.rs(uiMeta.getMainTable(), openNodeId).getString(uiMeta.getPathField()),
                    _trDepth
            );

            return Ret.success(dataList);
        } catch (Exception e) {
            return Ret.error(e);
        }
    }


    @GuestDisabled
    @RequestMapping("tree.ajax.{uid}.{btnId}")
    @ResponseBody
    public Ret<?> do_ajax(@PathVariable String uid, @PathVariable String btnId, String[] id) {
        return super.do_ajax("tree", uid, btnId, id);
    }

    @RequestMapping("tree.selectData.{uid}")
    @ResponseBody
    public Ret<?> getSelectTreeData(@PathVariable String uid, String parentId,
                                    String _trSkipSelfId, //树节点修改，选上级时要跳过自身id
                                    String value //当前选中项id
    ) {
        try {
            TreeUiMeta uiMeta;
            try {
                uiMeta = service.getUiMeta(uid);
            } catch (Exception ex) {
                return Ret.error(ex);
            }
            Args args = __createQuery(uiMeta, createFtlParser(), parentId);

            Dao dao = appContext.getDao();
            String selectedNodePath = "";
            if (StringUtils.hasText(value)) {
                Rs selectedNode = dao.rs(uiMeta.getMainTable(), value);
                selectedNodePath = selectedNode.getString(uiMeta.getPathField());
            }

            Pager<Rs> ls = dao.ls(args);

            List<String> selectedNodePathNames = new ArrayList<>();
            List<Map<String, Object>> lists = __wrapSelectNodes(ls, _trSkipSelfId, uiMeta, selectedNodePath, selectedNodePathNames);
            return Ret.success(lists, ArrayUtils.join(selectedNodePathNames, "/"));
        } catch (Exception e) {
            return Ret.error(e);
        }
    }

    private Args __createQuery(TreeUiMeta uiMeta, FtlParser parser, String parentId) throws Exception {
        //todo 实体方式查询
        String filter = StringUtils.hasText(parentId)
                ? "o." + uiMeta.getPidField() + "='" + parentId.trim() + "'"
                : "o." + uiMeta.getPidField() + uiMeta.getRootPid().getOperationString();
        if (StringUtils.hasText(uiMeta.getSqlFilter())) {
            filter += " and " + parser.parse(uiMeta.getSqlFilter());
        }
        filter += " order by o." + (StringUtils.hasText(uiMeta.getSortField()) ? uiMeta.getSortField() : "id") + " " +
                (StringUtils.hasText(uiMeta.getSortType()) ? uiMeta.getSortType() : "asc");

        return new Args(uiMeta.getMainTable() + " o", filter, "*");
    }

    private List<Rs> __wrapRowData(TreeUiMeta uiMeta, FtlParser parser, TreeHandler handler, List<Rs> ls,
                                   Map<String, Map<String, DictInfo>> kvMaps,
                                   Map<String, Rs> _ktbmap, String openNodePath, int depth) {
        List<Rs> dataList = new ArrayList<>();
        for (Rs rs : ls) {
            parser.addVariable("vo", rs);
            handler.onView(rs, depth);
            Rs rowData = rowPacket(rs, uiMeta.getColumns(), parser, kvMaps, _ktbmap, false);
            Pager<Rs> children = appContext.getDao().ls(new Args(uiMeta.getMainTable(), uiMeta.getPidField() + "='" + rs.getString("id") + "' order by " + uiMeta.getSortField() + " " + uiMeta.getSortType()));
            if (openNodePath != null && openNodePath.contains("." + rs.getString(rs.getPrimaryKey()) + ".") || !uiMeta.isLazy()) {
                List<Rs> items = __wrapRowData(uiMeta, parser, handler, children.getItems(), kvMaps, _ktbmap, openNodePath, depth + 1);
                rowData.put("children", items);
                rowData.put("open", true);
            } else {
                rowData.put("_haveChildren", children.getRowCount() > 0);
            }
            rowData.put("_trDepth", depth);

            //按钮显示条件控制:
            if (uiMeta.isMge()) {
                List<String> hiddenBtns = Arrays.stream(uiMeta.getRowButtons())
                        .filter(btn -> StringUtils.hasText(btn.getConditionShow()) && !JDiyLsController.ifCondition(btn, uiMeta, rs, parser))
                        .map(ButtonMeta::getId).collect(Collectors.toList());
                if (!hiddenBtns.isEmpty()) rs.put("_hiddenBtns", hiddenBtns.toString());
            }

            dataList.add(rowData);
        }
        return dataList;
    }


    private List<Map<String, Object>> __wrapSelectNodes(Pager<Rs> ls, String _trSkipSelfId, TreeUiMeta uiMeta,
                                                        String selectedNodePath, List<String> selectedNodePathNames) {
        List<Map<String, Object>> dataList = new ArrayList<>();
        for (Rs rs : ls.getItems()) {
            if (_trSkipSelfId != null && _trSkipSelfId.equals(rs.getString("id"))) continue;
            Map<String, Object> rowData = new LinkedHashMap<>();
            rowData.put("id", rs.get(rs.getPrimaryKey()));
            rowData.put("title", rs.get(uiMeta.getNameField()));

            Pager<Rs> children = appContext.getDao().ls(new Args(uiMeta.getMainTable(), uiMeta.getPidField() + "='" + rs.getString("id") + "' order by " + uiMeta.getSortField() + " " + uiMeta.getSortType()));
            boolean isSelectedPaths = StringUtils.hasText(selectedNodePath) && selectedNodePath.contains("." + rs.getString(rs.getPrimaryKey()) + ".");
            if (isSelectedPaths || !uiMeta.isLazy()) {
                if (isSelectedPaths) selectedNodePathNames.add(rs.getString(uiMeta.getNameField()));
                rowData.put("children", __wrapSelectNodes(children, _trSkipSelfId, uiMeta, selectedNodePath, selectedNodePathNames));
                rowData.put("spread", true);
            } else {
                rowData.put("lazy", children.getRowCount() > 0);
            }
            dataList.add(rowData);
        }
        return dataList;
    }


    @Resource
    private AppContext appContext;
    @Resource
    private Handlers handlers;
}
